﻿using SoundEngine3.AudioProcessing.Filters;
using System;
using ZquenceStudio.DefaultDspPlugins.Effects;

namespace DefaultDspPluginsLib.Depricated
{
    public class Effect_Delay
    {
        public class Holder
        {
            internal double[] bufferedDelay;
            internal int bufferedDelayLengh;
            internal int offsetTick = -1;
            internal FilterEQFast mEQs;

            internal Holder()
            {
                bufferedDelay = new double[0];
                mEQs = new FilterEQFast();
            }

            internal void SetSampleRate(double sampleRate)
            {
                mEQs.SampleRate = sampleRate;
                mEQs.Init();
            }

            internal void ClearBuffers()
            {
                offsetTick = 0;
                Array.Clear(bufferedDelay, 0, bufferedDelay.Length);
            }
        }
        
        private int _DelayTime;
        private int _delayBufferOffset;
        private bool _start_record;
        private int _old_BPM;
        private Holder[] mDelayChannel = new Holder[2];
        private double mDelay;
        
        public double DelayVolyme { get; set; }
        public double Band1 { get; set; }
        public double Band2 { get; set; }
        public double Band3 { get; set; }

        public Effect_Delay()
        {
            for (int i = 0; i < mDelayChannel.Length; i++)
            {
                mDelayChannel[i] = new Holder();
            }
            DelayVolyme = 0.8;
            Band1 = 0.8;
            Band2 = 0.8;
            Band3 = 0.8;
        }
        
        public void SetDelay(int value, double samplesPerBeat)
        {
            _DelayTime = value;
            mDelay = value;

            double samplesPerPPQ = samplesPerBeat;
            
            _delayBufferOffset = (int)(samplesPerPPQ * (value / 4.0));

            if (_delayBufferOffset == 0)
            {
                _delayBufferOffset = 1;
            }

            // Update the delay buffer on each channel.
            foreach (var item in mDelayChannel)
            {
                if (item.bufferedDelayLengh < _delayBufferOffset)
                {
                    item.bufferedDelayLengh = _delayBufferOffset;
                    item.bufferedDelay = new double[_delayBufferOffset];
                }
                else
                {
                    ClearBuffers();
                }
            }
        }

        public void ClearBuffers()
        {
            foreach (var eq in mDelayChannel)
            {
                eq.ClearBuffers();
            }
        }
        
        public void SetSampleRate(int sampleRate, int samplesPerBeat)
        {
            ClearBuffers();
            SetDelay(_DelayTime, samplesPerBeat);
            foreach (var eq in mDelayChannel)
            {
                eq.SetSampleRate(sampleRate);
            }
        }

        public void Process(ref Delay.DelayData data, double[] audioBufferLeft, double[] audioBufferRight, double[] audioBufferLeft_ouput, double[] audioBufferRight_output)
        {
            double _hi_volyme = Band3 * 0.8; // In real world hi-freq is a bit lower

            // Om BufferSize eller BPMen har ändrats så behöver vi ändra på Delayens buffer
            if (_old_BPM != data.Tempo)
            {
                SetDelay(_DelayTime, data.SamplesPerBeat);
                _old_BPM = (int)data.Tempo;
            }

            for (int c = 0; c < data.channels; c++)
            {
                Holder holder = mDelayChannel[c];
                FilterEQFast eq = holder.mEQs;
                eq.low_volyme = Band1;
                eq.mid_volyme = Band2;
                eq.hi_volyme = _hi_volyme;
                double[] bd = holder.bufferedDelay;
                double[] smInput = c == 0 ? audioBufferLeft : audioBufferRight;
                double[] smOutput = c == 0 ? audioBufferLeft_ouput : audioBufferRight_output;
                int length = smInput.Length;

                for (int i = 0; i < length; ++i)
                {
                    if (++holder.offsetTick >= _delayBufferOffset)
                    {
                        holder.offsetTick = 0;
                        _start_record = true;
                    }

                    if (_start_record)
                    {
                        smOutput[i] += bd[holder.offsetTick] * DelayVolyme;
                    }
                    
                    // Process.
                    eq.L = eq.D2 + eq.F1 * eq.D1;
                    eq.H = smInput[i] - eq.L - eq.Q1 * eq.D1;
                    eq.B = eq.F1 * eq.H + eq.D1;

                    // store delays
                    eq.D1 = eq.B;
                    eq.D2 = eq.L;

                    // outputs // L + H + B
                    bd[holder.offsetTick] = eq.L * Band1 + eq.B * Band2 + eq.H * _hi_volyme;
                }
            }
        }

        public void Update(double samplesPerBeat, int mDelayTime, double mDelayVolyme, double mBand1, double mBand2, double mBand3)
        {
            SetDelay(mDelayTime, samplesPerBeat);
            DelayVolyme = mDelayVolyme;
            Band1 = mBand1;
            Band2 = mBand2;
            Band3 = mBand3;
        }
    }
}
